package pers.zb.cloud.service.upms.aop.datasource;

import org.springframework.beans.BeansException;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;
import pers.zb.cloud.service.upms.util.BeanUtil;

import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;
 
/**
 * 多数据源配置
 *     自动加载 application.yml 中配置的datasource，动态注册数据源。
 *     实现ApplicationContextAware这个接口的目的，主要是获取applicationContext上下文对象，向BeanUtil工具类中注入applicationContext对象，就可以使用该工具类动态创建spring bean。
 */
@Component
public class DataSourceConfig implements ApplicationContextAware {

    //该对象存储的是 application.yml 文件中配置的多个数据源spring.datasource的配置信息
    private DataSourcesProperties dataSourcesProperties;

    //存储TransactionManager事务管理器的信息
    public static  Map<String,String> TRANSACTION_MANAGERMAP = new HashMap<>();
    //默认的事务管理器名称
    public static  String DEFAULT_TRANSACTION_MANAGER_NAME;
    //默认数据源名称
    public static  String DEFAULT_DATASOURCE_NAME;
    /**
     * 动态数据源: 通过AOP在不同数据源之间动态切换
     */
    @Primary
    @Bean
    public DataSource dynamicDataSource() {
        DynamicDataSource dynamicDataSource = new DynamicDataSource();
        Map<Object, Object> dsMap = new HashMap();// 配置多数据源
        Map<String, DataSourcesProperties.DataSourcePro> dataSourceProMap = dataSourcesProperties.getList();
        for (String key : dataSourceProMap.keySet()) {
            DataSourcesProperties.DataSourcePro dataSourcePro = dataSourceProMap.get(key);
            DataSource dataSource = createDataSource(dataSourcePro.getUsername(),dataSourcePro.getPassword(),dataSourcePro.getJdbcUrl(),dataSourcePro.getDriverClassName());
            if(dataSourcePro.isDefaultTarget()){//defaultTarget是application.yml数据源配置的一个参数属性
                // 默认数据源
                dynamicDataSource.setDefaultTargetDataSource(dataSource);
                DEFAULT_TRANSACTION_MANAGER_NAME = key + "_transactionManager";
                DEFAULT_DATASOURCE_NAME = key;
            }
            dsMap.put(key, dataSource);

            /**
             * 通过BeanUtil工具类动态注册bean
             *      如果通过@Bean注解硬编码来注册TransactionManager事务管理器的话，感觉有点不太优雅，就选择了这种动态注册bean的方式。
             *      无论application.yml中如何变更数据源配置，这里的代码都无需修改。所以优点就是解耦合。
             */
            Map<String, Object> propertyMap = new HashMap<String, Object>();
            propertyMap.put("DataSource",dataSource);//创建DataSourceTransactionManager这个Bean的话，必须需要注入一个DataSource属性，看源码也能看到。
            BeanUtil.addBean(
                    "org.springframework.jdbc.datasource.DataSourceTransactionManager", //需要创建的Bean
                    key + "_transactionManager",  //bean的name，也就是别名
                    propertyMap, //创建Bean对象需要的注入的属性
                    null, null, null, null);

            TRANSACTION_MANAGERMAP.put(key,key + "_transactionManager");
        }
        dynamicDataSource.setTargetDataSources(dsMap);
        return dynamicDataSource;
    }

    /**
     * 项目启动过程中，获取上下文对象，从applicationContext中获取对应的bean（DataSourcesProperties）
     */
    private static ApplicationContext applicationContext = null;
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        if(this.applicationContext == null){
            this.applicationContext  = applicationContext;
            BeanUtil.init(applicationContext);
        }
        dataSourcesProperties = this.applicationContext.getBean(DataSourcesProperties.class);
    }

    /**
     * 创建数据源
     */
    private DataSource createDataSource(String username,String password,String url,String driverClassName) {
        return DataSourceBuilder.create()
                .url(url)
                .username(username)
                .password(password)
                .driverClassName(driverClassName)
                .build();
    }
}

